﻿/*----------------------------------------------------------------
//  <copyright file="ConnectionUserCache.cs" company="MicroCloud@151504200868">
//      Copyright © 2020-2024 MicroCloud Corporation, All rights reserved.
//  </copyright>
//  <site>https://gitee.com/chenmm123/microclouds</site>
//  <last-editor>cmm</last-editor>
//  <last-date>2024-03-04 14:42</last-date>
//----------------------------------------------------------------*/

namespace MicroCloud.AspNetCore.SignalR
{
    #region "SignalR连接用户缓存"
    /// <summary>
    /// SignalR连接用户缓存
    /// </summary>
    public class ConnectionUserCache : IConnectionUserCache
    {
        //字段
        private readonly IDistributedCache _cache;
        private readonly static List<ConnectionUserGroup> _userGroup = new();

        #region "构造函数"
        #region "初始化一个SignalR连接用户缓存的新实例"
        /// <summary>
        /// 初始化一个SignalR连接用户缓存 <see cref="ConnectionUserCache"/> 的新实例
        /// </summary>
        /// <param name="cache"></param>
        public ConnectionUserCache(IDistributedCache cache)
        {
            _cache = cache;
        }
        #endregion

        #endregion

        #region "方法"
        //用户
        #region "获取缓存的用户信息"
        /// <summary>
        /// 获取缓存的用户信息
        /// </summary>
        /// <param name="userKey">用户标识</param>
        /// <returns></returns>
        public virtual async Task<ConnectionUser> GetUser(string userKey)
        {
            Check.NotNullOrEmpty(userKey, nameof(userKey));

            string key = GetKey(userKey);
            return await _cache.GetAsync<ConnectionUser>(key);
        }
        #endregion
        #region "获取指定用户的所有连接Id"
        /// <summary>
        /// 获取指定用户的所有连接Id
        /// </summary>
        /// <param name="userKey">用户标识</param>
        /// <returns></returns>
        public virtual async Task<string[]> GetConnectionIds(string userKey)
        {
            Check.NotNullOrEmpty(userKey, nameof(userKey));

            ConnectionUser user = await GetUser(userKey);
            return user?.ConnectionIds.ToArray() ?? Array.Empty<string>();
        }
        #endregion
        #region "设置用户缓存"
        /// <summary>
        /// 设置用户缓存
        /// </summary>
        /// <param name="userKey">用户标识</param>
        /// <param name="user">用户信息</param>
        /// <returns></returns>
        public virtual async Task<ConnectionUser> SetUser(string userKey, ConnectionUser user)
        {
            Check.NotNullOrEmpty(userKey, nameof(userKey));
            Check.NotNull(user, nameof(user));
            Check.NotNullOrEmpty(user.UserKey, nameof(user.UserKey));
            Check.NotNull(user.ConnectionIds, nameof(user.ConnectionIds));
            if (user.ConnectionIds.Count <= 0)
            {
                throw new Exception(I18N.T("设置SignalR连接用户缓存时 {0} 集合数必须大于0", nameof(user.ConnectionIds)));
            }

            string key = GetKey(userKey);
            await _cache.SetAsync(key, user);

            return await _cache.GetAsync<ConnectionUser>(key);
        }
        #endregion
        #region "添加指定用户的连接"
        /// <summary>
        /// 添加指定用户的连接
        /// </summary>
        /// <param name="userKey">用户标识</param>
        /// <param name="connectionId">连接Id</param>
        /// <returns></returns>
        public virtual async Task<ConnectionUser> AddConnectionId(string userKey, string connectionId)
        {
            Check.NotNullOrEmpty(userKey, nameof(userKey));
            Check.NotNullOrEmpty(connectionId, nameof(connectionId));

            ConnectionUser user = await GetUser(userKey) ?? new ConnectionUser() { UserKey = userKey, OnlineStatus = OnlineStatus.Online };
            user.ConnectionIds.AddIfNotExist(connectionId);
            await SetUser(userKey, user);

            return await GetUser(userKey);
        }
        #endregion
        #region "移除指定用户的连接"
        /// <summary>
        /// 移除指定用户的连接
        /// </summary>
        /// <param name="userKey">用户标识</param>
        /// <param name="connectionId">连接Id</param>
        /// <returns></returns>
        public virtual async Task<ConnectionUser> RemoveConnectionId(string userKey, string connectionId)
        {
            Check.NotNullOrEmpty(userKey, nameof(userKey));
            Check.NotNullOrEmpty(connectionId, nameof(connectionId));

            ConnectionUser user = await GetUser(userKey);
            if (user == null || !user.ConnectionIds.Contains(connectionId))
            {
                return user;
            }
            user.ConnectionIds.Remove(connectionId);
            RemoveUserGroupByConnection(connectionId);
            if (user.ConnectionIds.Count == 0)
            {
                await RemoveUser(userKey);
                return null;
            }
            await SetUser(userKey, user);

            return await GetUser(userKey);
        }
        #endregion
        #region "移除指定用户的缓存"
        /// <summary>
        /// 移除指定用户的缓存
        /// </summary>
        /// <param name="userKey">用户标识</param>
        /// <returns></returns>
        public virtual async Task RemoveUser(string userKey)
        {
            Check.NotNullOrEmpty(userKey, nameof(userKey));

            string key = GetKey(userKey);
            await _cache.RemoveAsync(key);
            RemoveUserGroupByUser(userKey);
        }
        #endregion
        //组
        #region "获取指定用户组信息集合"
        /// <summary>
        /// 获取指定用户组信息集合
        /// </summary>
        /// <param name="userKey">用户标识</param>
        /// <returns></returns>
        public virtual List<ConnectionUserGroup> GetUserGroupByUser(string userKey)
        {
            Check.NotNullOrEmpty(userKey, nameof(userKey));

            return _userGroup.FindAll(o => o.UserKey == userKey);
        }
        #endregion
        #region "获取指定组用户信息集合"
        /// <summary>
        /// 获取指定组用户信息集合
        /// </summary>
        /// <param name="groupKey">组标识</param>
        /// <returns></returns>
        public virtual List<ConnectionUserGroup> GetUserGroupByGroup(string groupKey)
        {
            Check.NotNullOrEmpty(groupKey, nameof(groupKey));

            return _userGroup.FindAll(o => o.GroupKey == groupKey);
        }
        #endregion
        #region "获取指定连接组信息集合"
        /// <summary>
        /// 获取指定连接组信息集合
        /// </summary>
        /// <param name="connectionId">连接ID</param>
        /// <returns></returns>
        public virtual List<ConnectionUserGroup> GetUserGroupByConnection(string connectionId)
        {
            Check.NotNullOrEmpty(connectionId, nameof(connectionId));

            return _userGroup.FindAll(o => o.ConnectionId == connectionId);
        }
        #endregion
        #region "设置用户组"
        /// <summary>
        /// 设置用户组
        /// </summary>
        /// <param name="userGroup">用户组</param>
        /// <returns></returns>
        public virtual ConnectionUserGroup SetUserGroup(ConnectionUserGroup userGroup)
        {
            Check.NotNull(userGroup, nameof(userGroup));
            Check.NotNullOrEmpty(userGroup.UserKey, nameof(userGroup.UserKey));
            Check.NotNullOrEmpty(userGroup.GroupKey, nameof(userGroup.GroupKey));
            Check.NotNullOrEmpty(userGroup.ConnectionId, nameof(userGroup.ConnectionId));

            _userGroup.AddIfNotExist(userGroup);

            return userGroup;
        }
        #endregion
        #region "添加指定的用户组"
        /// <summary>
        /// 添加指定的用户组
        /// </summary>
        /// <param name="userKey">用户标识</param>
        /// <param name="groupKey">组标识</param>
        /// <param name="connectionId">连接Id</param>
        /// <returns></returns>
        public virtual ConnectionUserGroup AddUserGroup(string userKey, string groupKey, string connectionId)
        {
            Check.NotNullOrEmpty(userKey, nameof(userKey));
            Check.NotNullOrEmpty(groupKey, nameof(groupKey));
            Check.NotNullOrEmpty(connectionId, nameof(connectionId));

            var userGroup = new ConnectionUserGroup() { UserKey = userKey, GroupKey = groupKey, ConnectionId = connectionId };

            return SetUserGroup(userGroup);
        }
        #endregion
        #region "移除指定的用户组"
        /// <summary>
        /// 移除指定的用户组
        /// </summary>
        /// <param name="userKey">用户标识</param>
        /// <param name="groupKey">组标识</param>
        /// <param name="connectionId">连接Id</param>
        public virtual void RemoveUserGroup(string userKey, string groupKey, string connectionId)
        {
            Check.NotNullOrEmpty(userKey, nameof(userKey));
            Check.NotNullOrEmpty(groupKey, nameof(groupKey));
            Check.NotNullOrEmpty(connectionId, nameof(connectionId));

            _userGroup.RemoveAll(o => o.UserKey == userKey && o.GroupKey == groupKey && o.ConnectionId == connectionId);
        }
        #endregion
        #region "移除指定用户的用户组信息"
        /// <summary>
        /// 移除指定用户的用户组信息
        /// </summary>
        /// <param name="userKey">用户标识</param>
        public virtual void RemoveUserGroupByUser(string userKey)
        {
            Check.NotNullOrEmpty(userKey, nameof(userKey));

            _userGroup.RemoveAll(o => o.UserKey == userKey);
        }
        #endregion
        #region "移除指定组的用户组信息"
        /// <summary>
        /// 移除指定组的用户组信息
        /// </summary>
        /// <param name="groupKey">组标识</param>
        public virtual void RemoveUserGroupByGroup(string groupKey)
        {
            Check.NotNullOrEmpty(groupKey, nameof(groupKey));

            _userGroup.RemoveAll(o => o.GroupKey == groupKey);
        }
        #endregion
        #region "移除指定连接的用户组信息"
        /// <summary>
        /// 移除指定连接的用户组信息
        /// </summary>
        /// <param name="connectionId">连接ID</param>
        public virtual void RemoveUserGroupByConnection(string connectionId)
        {
            Check.NotNullOrEmpty(connectionId, nameof(connectionId));

            _userGroup.RemoveAll(o => o.ConnectionId == connectionId);
        }
        #endregion

        #endregion

        #region "私有静态方法"
        #region "获取连接键值"
        /// <summary>
        /// 获取连接键值
        /// </summary>
        /// <param name="userKey">用户键标识</param>
        /// <returns></returns>
        private static string GetKey(string userKey)
        {
            return $"SignalR:Connection_User:{userKey}";
        }
        #endregion

        #endregion

    }
    #endregion

}
